require('module-alias/register'); // 配置文件路径别名 在package.json 中配置 "dependencies:module-alias" + "_moduleAliases:{}"
require('express-async-errors'); // 用于解决异步异常无法捕获的问题

const express = require('express'); // 框架
const bodyParser = require('body-parser'); // 中间件-解析客户端请求的内容
const session = require("express-session"); // 中间件-session
const cookieParser = require("cookie-parser"); // 中间件-cookie

const response = require('module/response'); // 响应模块

// 路由
const userRouter = require('./routes/user'); // 用户
const fileRouter = require('./routes/file'); // 文件
const flashRouter = require('./routes/flash'); // flash
const commentRouter = require('./routes/comment'); // 评论
const emailRouter = require('./routes/email'); // 邮件

const app = express(); // Express应用程序实例对象

// 中间件-cookie
app.use(cookieParser());
// 配置session express中是把session信息存储在内存中
app.use(session({
  secret: 'flash session', // String 类型的字符串，作为服务器端生成 session 的签名。
  // name: 'name', // 返回客户端的 key 的名称，默认为 connect.sid,也可以自己设置。
  // 设置返回到前端 key 的属性，默认值为{ path: ‘/’, httpOnly: true, secure: false, maxAge: null }。
  cookie: {
    maxAge: 30 * 60 * 1000, // 超时时长
  },
  resave: false,  // 强制保存 session 即使它并没有变化,。默认为 true。建议设置成 false。 don't save session if unmodified
  saveUninitialized: false, // 强制将未初始化的 session 存储。当新建了一个 session 且未设定属性或值时，它就处于未初始化状态。在设定一个 cookie 前，这对于登陆验证，减轻服务端存储压力，权限控制是有帮助的。(默 认:true)。建议手动添加。
  // rolling: false // 在每次请求时强行设置 cookie，这将重置 cookie 过期时间(默认:false)
}));

// 中间件-解析客户端请求的内容
app.use(bodyParser.json()) // JSON编码处理
app.use(bodyParser.urlencoded({ extended: false })); // URL编码处理

// 中间件-跨域，必须在路由之前挂载
app.use(crossDomain);
// 中间件-登录状态过滤
app.use(loggingStatus);
// 中间件-超时处理
app.use(settingTimeout);

/* // express-async-errors 异步函数异常捕捉实例
app.use(async (req, res) => {
  throw Error("access denied");
}); */

// 中间件-把路由挂载到app中
app.use('/user', userRouter);
app.use('/file', fileRouter);
app.use('/flash', flashRouter);
app.use('/comment', commentRouter);
app.use('/email', emailRouter);

// 中间件-捕获并响应404，必须在路由之后挂载，当匹配不到对应路由时进入404
app.use(handle404);
// 中间件-错误处理
app.use(handleError);

module.exports = app;

/**
 * 中间件-跨域
 * @param {Object} req
 * @param {Object} res
 * @param {Object} next
 * 踩坑记录-前端请求如果携带Cookie信息，那么后端 Access-Control-Allow-Origin 和 Access-Control-Allow-Headers 不能为*
 */
function crossDomain(req, res, next) {
  // let siteList = ['http://172.16.59.190:9001', 'http://172.16.59.190:8848/index.html'];
  // 设置允许跨域的域名，*代表允许任意域名跨域  使用cookie不能使用*号通配符
  // res.header('Access-Control-Allow-Origin', 'http://127.0.0.1:9001');
  res.header('Access-Control-Allow-Origin', '*');
  // 跨域允许的请求方式
  res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
  // 允许的header类型
  res.header('Access-Control-Allow-Headers', 'Content-Type,token');
  // res.header('Access-Control-Allow-Headers', 'Content-Type,Content-Length,X-Requested-With,Authorization,Accept,token');
  // 允许跨域请求携带cookie
  res.header('Access-Control-Allow-Credentials', 'true');
  if (req.method.toLowerCase() === 'options') {
    res.sendStatus(200); // 让options尝试请求快速结束
  } else {
    next();
  }
}

/**
 * 中间件-登录状态过滤
 * @param {Object} req
 * @param {Object} res
 * @param {Object} next
 */
function loggingStatus(req, res, next) {
  // 需要登录才能访问的接口列表
  let astrictUrls = [
    '/file/uploadFile/',
    '/flash/add',
    '/flash/update',
    '/flash/remove',
    '/comment/audit',
    '/comment/remove'
  ]
  if (req.session.username) {
    next();
  } else {
    // 循环判断是否包含指定url
    let includeUrl = false;
    for (let item of astrictUrls) {
      if (req.url.includes(item)) {
        includeUrl = true;
        break;
      }
    }
    if (includeUrl) {
      response(res, null, null, 9000);
    } else {
      next();
    }
  }
}

/**
 * 设置请求+响应超时 1分钟
 * @param {Object} req
 * @param {Object} res
 * @param {Object} next
 */
function settingTimeout(req, res, next) {
  // 设置所有HTTP请求的超时时间
  req.setTimeout(60000, () => {
    response(res, null, null, 408);
  });
  // 设置所有HTTP请求的服务器响应超时时间
  res.setTimeout(60000, () => {
    response(res, null, null, 408);
  });
  next();
}

/**
 * 中间件-捕获并响应404
 * @param {Object} req
 * @param {Object} res
 * @param {Object} next
 */
function handle404(req, res, next) {
  response(res, null, null, 404);
}

/**
 * 中间件-错误处理
 * @param {Object} err
 * @param {Object} req
 * @param {Object} res
 * @param {Object} next
 */
function handleError(err, req, res, next) {
  // 设置局部变量，只在开发中提供错误
  // res.locals.error = req.app.get('env') === 'development' ? err : {};
  // 返回错误信息
  response(res, null, err, 500);
}
